//
//  GCDViewController.m
//  多线程
//
//  Created by hfzhangzhang on 2018/4/9.
//  Copyright © 2018年 hfzhangzhang. All rights reserved.
//

#import "GCDViewController.h"

@interface GCDViewController ()

@end

@implementation GCDViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    //1.创建队列
    //2.任务添加到队列
    
}
/**
 
 
 什么是GCD
 全称是Grand Central Dispatch，可译为“牛逼的中枢调度器”
 纯C语言，提供了非常多强大的函数
 
 GCD的优势
 GCD是苹果公司为多核的并行运算提出的解决方案
 GCD会自动利用更多的CPU内核（比如双核、四核）
 GCD会自动管理线程的生命周期（创建线程、调度任务、销毁线程）
 程序员只需要告诉GCD想要执行什么任务，不需要编写任何线程管理代码
 
 GCD中有2个用来执行任务的常用函数
 用同步的方式执行任务
 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
 queue：队列
 block：任务
 
 用异步的方式执行任务
 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
 
 
 GCD中还有个用来执行任务的函数：
 dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
 在前面的任务执行结束后它才执行，而且它后面的任务等它执行完成之后才会执行
 这个queue不能是全局的并发队列
 
 
 一个dispatch barrier 允许在一个并发队列中创建一个同步点。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block,等待所有在barrier之前提交的blocks执行结束。 这时，barrier block自己开始执行。 之后， 队列继续正常的执行操作。
 
 调用这个函数总是在barrier block被提交之后立即返回，不会等到block被执行。当barrier block到并发队列的最前端，他不会立即执行。相反，队列会等到所有当前正在执行的blocks结束执行。到这时，barrier才开始自己执行。所有在barrier block之后提交的blocks会等到barrier block结束之后才执行。
 
 这里指定的并发队列应该是自己通过dispatch_queue_create函数创建的。如果你传的是一个串行队列或者全局并发队列，这个函数等同于dispatch_async函数。
 

 
 
 并发和串行主要影响：任务的执行方式
 

 
 注意
 使用sync函数往当前串行队列中添加任务，会卡住当前的串行队列

 
 
 使用dispatch_queue_create函数创建队列
 dispatch_queue_t
 dispatch_queue_create(const char *label, // 队列名称
 dispatch_queue_attr_t attr); // 队列的类型
 
 使用dispatch_get_global_queue函数获得全局的并发队列
 dispatch_queue_t dispatch_get_global_queue(
 dispatch_queue_priority_t priority, // 队列的优先级
 unsigned long flags); // 此参数暂时无用，用0即可
 
 使用主队列（跟主线程相关联的队列）
 主队列是GCD自带的一种特殊的串行队列
 放在主队列中的任务，都会放到主线程中执行
 使用dispatch_get_main_queue()获得主队列
 dispatch_queue_t queue = dispatch_get_main_queue();


 有4个术语比较容易混淆：同步、异步、并发、串行
 
 同步和异步主要影响：能不能开启新的线程
 
 同步：只是在当前线程中执行任务，不具备开启新线程的能力
 异步：可以在新的线程中执行任务，具备开启新线程的能力
 
 并发：允许多个任务并发（同时）执行，并发功能只有在异步（dispatch_async）函数下才有效
 串行：一个任务执行完毕后，再执行下一个任务
 


 
 GCD 核心概念:将任务添加到队列,指定任务执行的方法
 - 任务
 - 使用block 封装
 - block 就是一个提前准备好的代码块,在需要的时候执行
 - 队列(负责调度任务)
 
 - 串行队列: 必须等待一个任务执行完成,在调度另外一个任务，除了主线程最多只能开启一条线程， 主队列除外只有主线程一个线程。
 - 并发队列: 可以同时调度多个任务
 
 - 主队列：串行队列且只有主线程一个线程，同步和异步一样的，都是同步执行的，以先进先出调度任务,如果主线程上有任务在执行,主队列不会调度任务，异步会把方法放入最后执行，同步会死锁
 
 同步执行，任务执行完成才能下一步，不会开启线程，顺序执行，利用同步任务,能够做到,任务依赖关系,前一个任务不执行完成,队列就不会调度后面的任务
 异步执行，任务执行没完成，可以不动等待，异步执行下一个任务，具备开启线程的能力，不移动开启
 
 
 - 任务执行函数(任务都需要在线程中执行!!)
 - 同步执行: 不会到线程池里面去获取子线程!
 - 异步执行: 只要有任务,哥么就会到线程池取子线程!(主队列除外!)
 小结:
 - 开不开线程,取决于执行任务的函数,同步不开,异步才能开
 - 开几条线程,取决于队列,串行开一条,并发可以开多条(异步)
 
 
 
 
 
 队列组
 
 有这么1种需求
 首先：分别异步执行2个耗时的操作
 其次：等2个异步操作都执行完毕后，再回到主线程执行操作
 
 如果想要快速高效地实现上述需求，可以考虑用队列组
 dispatch_group_t group =  dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 // 执行1个耗时的异步操作
 });
 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 // 执行1个耗时的异步操作
 });
 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
 // 等前面的异步操作都执行完毕后，回到主线程...
 });
 
 
 dispatch_semaphore_create 创建一个semaphore
 dispatch_semaphore_signal 发送一个信号
 dispatch_semaphore_wait 等待信号
 简单的介绍一下这三个函数，第一个函数有一个整形的参数，我们可以理解为信号的总量，dispatch_semaphore_signal是发送一个信号，自然会让信号总量加1，dispatch_semaphore_wait等待信号，当信号总量少于0的时候就会一直等待，否则就可以正常的执行，并让信号总量-1，根据这样的原理，我们便可以快速的创建一个并发控制来同步任务和有限资源访问控制。

 
 */

-(void)GCDDemo1
{
    dispatch_queue_t q = dispatch_queue_create(0, 0);
    dispatch_sync(q, ^{
        NSLog(@"----%@",[NSThread currentThread]);//主线程
    });
}

-(void)GCDDemo2
{
    
    dispatch_queue_t q = dispatch_queue_create(0, 0);
    dispatch_async(q, ^{
        NSLog(@"----%@",[NSThread currentThread]);//非线程
    });
}

-(void)GCDDemo3
{
    
    /** 全局队列 (本质上就是并发队列)
     参数:
     1.涉及到系统适配:
     iOS 8       服务质量
     - QOS_CLASS_USER_INTERACTIVE                        用户交互(用户迫切希望线程快点)
     - QOS_CLASS_USER_INITIATED                          用户需要的(同样不要使用耗时操作)
     - QOS_CLASS_DEFAULT                                 默认的(给系统用来重置队列的)
     - QOS_CLASS_UTILITY                                 实用工具(用来做耗时操作的)
     - QOS_CLASS_BACKGROUND                              后台
     - QOS_CLASS_UNSPECIFIED                             没有指定优先级
     iOS 7       调度的优先级
     - DISPATCH_QUEUE_PRIORITY_HIGH 2                    高优先级
     - DISPATCH_QUEUE_PRIORITY_DEFAULT 0                 默认优先级
     - DISPATCH_QUEUE_PRIORITY_LOW (-2)                  低优先级
     - DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN      后台优先级
     因为我们项目大部分考虑 适配 iOS7 & iOS8 & iOS9 无法使用 服务质量,直接指定 0.
     友情提示:不要使用 BACKGROUND 优先级&服务质量 ,速度慢得令人发指!!
     2.为未来使用保留的,应该 传入 0
     */
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"----%@",[NSThread currentThread]);//子线程
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"UI----%@",[NSThread currentThread]);//主线程
        });
        
    });
}

//MARK 并发队列和串行队列


-(void)GCDDemo4{
    /**
     参数:
     1. 队列的名称
     2. 队列的属性
     DISPATCH_QUEUE_SERIAL     NULL 表示串行
     DISPATCH_QUEUE_CONCURRENT      表示并发
     */
    //串行同步执行-->顺序执行,不开线程
    dispatch_queue_t q0 = dispatch_queue_create("zhangzhang1", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(q0, ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"%@  %d",[NSThread currentThread],i);
        }
    });
    
    //串行异步执行-->顺序执行,开线程，主线程外，会开1条线程
    dispatch_queue_t q1 = dispatch_queue_create("zhangzhang1", NULL);
    for (int i = 0; i < 10; i++) {
        NSLog(@"%d--------",i);
        dispatch_async(q1, ^{
            NSLog(@"%@  %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"come here");
    
    //并发队列异步执行-->不会顺序执行,开线程，会开多条线程
    dispatch_queue_t q3 = dispatch_queue_create("tanzhou", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i++) {
        dispatch_async(q3, ^{
            NSLog(@"%@  %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"come here");
    
    //并发队列同步执行-->会顺序执行,不开线程
    dispatch_queue_t q = dispatch_queue_create("tanzhou", DISPATCH_QUEUE_CONCURRENT);
    //2.同步执行
    for (int i = 0; i < 10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@  %d",[NSThread currentThread],i);
        });
    }
    NSLog(@"come here");
    
}
-(void)GCDDemo5{
    //阻塞主线程!!主队列,同步任务
    dispatch_queue_t q = dispatch_get_main_queue();
    NSLog(@"这里!!");
    //2.同步任务
    dispatch_sync(q, ^{
        NSLog(@"能来吗?");
    });
    NSLog(@"come here -- %@",[NSThread currentThread]);
    
    //全局队列,异步任务，不会死锁
    dispatch_async(dispatch_get_global_queue(0, 0),  ^{
        //1.队列  -> 一启动就有主线程,主队列只需要获取
        dispatch_queue_t q = dispatch_get_main_queue();
        NSLog(@"这里!!");
        //2.同步任务
        dispatch_sync(q, ^{
            NSLog(@"睡会");
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"能来吗?");
        });
        NSLog(@"come here -- %@",[NSThread currentThread]);
    });
}



-(void)delayDemo{
    
    NSLog(@"come here");
    /**
     *  参数
     从现在开始,经过多少纳秒之后,让queue队列,调度block任务,异步执行!!
     提示:
     - 这个函数在开发中经常用到!
     - 也经常用这个来将代码放子线程做测试
     1. when
     2. queue
     3. block()
     */
    // 从现在开始,经过多少  纳秒 之后
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
    dispatch_after(when, dispatch_queue_create("hank", NULL), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    
//    iOS常见的延时执行
//    调用NSObject的方法
//    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
//    // 2秒后再调用self的run方法
//
//    使用GCD函数
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//        // 2秒后执行这里的代码...
//    });
//
//    使用NSTimer
//    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:NO];

    
}

-(void)onceDemo{
    //GCD提供了一个一次执行的机制,不仅能够保证只会被执行一次.而且是线程安全的!
    //GCD-Once 内部是会加锁!! 但是 比普通的互斥锁 效率高，苹果推荐使用这个
    static dispatch_once_t onceToken;
    NSLog(@"%ld",onceToken);
    dispatch_once(&onceToken, ^{
        //只会执行一次的代码
        NSLog(@"执行了!!--%@",[NSThread currentThread]);
    });
    
//    使用dispatch_apply函数能进行快速迭代遍历
//    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){
//        // 执行10次代码，index顺序不确定
//    });

    
}

//MARK: 调度组
-(void)groupDemo{
    //1.队列
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    //2.调度组
    dispatch_group_t g = dispatch_group_create();
    //3.添加任务,让队列执行,任务执行情况最终通知组
    
    dispatch_group_async(g, q, ^{
        NSLog(@"download A %@",[NSThread currentThread]);
    });
    
    dispatch_group_async(g, q, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"download B %@",[NSThread currentThread]);
    });
    
    dispatch_group_async(g, q, ^{
        [NSThread sleepForTimeInterval:0.8];
        NSLog(@"download C %@",[NSThread currentThread]);
    });
    
    //4. 所有任务执行完毕后,获得通知
    //用一个调度组,可以监听全局队列调度的任务,执行完毕之后,在主队列执行最终处理!
    //dispatch_group_notify 本身是异步的
    dispatch_group_notify(g, dispatch_get_main_queue(), ^{
        //更新UI,通知用户
        NSLog(@"OK  %@",[NSThread currentThread]);
    });
    
//    NSLog(@"come here");
    
    
//    _block BOOL isok = NO;
//
//    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
//    Engine *engine = [[Engine alloc] init];
//    [engine queryCompletion:^(BOOL isOpen) {
//        isok = isOpen;
//        dispatch_semaphore_signal(sema);
//    } onError:^(int errorCode, NSString *errorMessage) {
//        isok = NO;
//        dispatch_semaphore_signal(sema);
//    }];
//
//    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
//    // todo what you want to do after net callback
    
//    // 创建队列组
//    dispatch_group_t group = dispatch_group_create();
//    // 创建信号量，并且设置值为10
//    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
//    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//    for (int i = 0; i < 100; i++)
//    {   // 由于是异步执行的，所以每次循环Block里面的dispatch_semaphore_signal根本还没有执行就会执行dispatch_semaphore_wait，从而semaphore-1.当循环10此后，semaphore等于0，则会阻塞线程，直到执行了Block的dispatch_semaphore_signal 才会继续执行
//        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//        dispatch_group_async(group, queue, ^{
//            NSLog(@"%i",i);
//            sleep(2);
//            // 每次发送信号则semaphore会+1，
//            dispatch_semaphore_signal(semaphore);
//        });
//    }

    
}



@end
